﻿using System.Runtime.InteropServices;
using MathS = System.Math;

#if F32
	using Num = System.Single;
	namespace Reign.Core.MathF32
#endif
#if F64
	using Num = System.Double;
	namespace Reign.Core.MathF64
#endif
#if I32
	using Num = System.Int32;
	namespace Reign.Core.MathI32
#endif
{
	[StructLayout(LayoutKind.Sequential)]
	public struct Matrix2
	{
		#region Properties
		public Vector2 X, Y;
		#endregion

		#region Constructors
		public Matrix2(Vector2 x, Vector2 y)
		{
			X = x;
			Y = y;
		}
		public Matrix2(Num value)
		{
			X = new Vector2(value);
			Y = new Vector2(value);
		}
		public Matrix2(Vector2 x)
		{
			X = x.Normalize();
			Y.X = -X.Y;
			Y.Y = X.X;
		}
		#endregion

		#region Operators
		// +
		public static Matrix2 operator+(Matrix2 p1, Matrix2 p2) {return new Matrix2(p1.X+p2.X, p1.Y+p2.Y);}
		public static Matrix2 operator+(Matrix2 p1, Num p2) {return new Matrix2(p1.X+p2, p1.Y+p2);}
		public static Matrix2 operator+(Num p1, Matrix2 p2) {return new Matrix2(p1+p2.X, p1+p2.Y);}
		// -
		public static Matrix2 operator-(Matrix2 p1, Matrix2 p2) {return new Matrix2(p1.X-p2.X, p1.Y-p2.Y);}
		public static Matrix2 operator-(Matrix2 p1, Num p2) {return new Matrix2(p1.X-p2, p1.Y-p2);}
		public static Matrix2 operator-(Num p1, Matrix2 p2) {return new Matrix2(p1-p2.X, p1-p2.Y);}
		// *
		public static Matrix2 operator*(Matrix2 p1, Matrix2 p2) {return new Matrix2(p1.X*p2.X, p1.Y*p2.Y);}
		public static Matrix2 operator*(Matrix2 p1, Num p2) {return new Matrix2(p1.X*p2, p1.Y*p2);}
		public static Matrix2 operator*(Num p1, Matrix2 p2) {return new Matrix2(p1*p2.X, p1*p2.Y);}
		// /
		public static Matrix2 operator/(Matrix2 p1, Matrix2 p2) {return new Matrix2(p1.X/p2.X, p1.Y/p2.Y);}
		public static Matrix2 operator/(Matrix2 p1, Num p2) {return new Matrix2(p1.X/p2, p1.Y/p2);}
		public static Matrix2 operator/(Num p1, Matrix2 p2) {return new Matrix2(p1/p2.X, p1/p2.Y);}
		// ==
		public static bool operator==(Matrix2 p1, Matrix2 p2) {return (p1.X==p2.X && p1.Y==p2.Y);}
		public static bool operator!=(Matrix2 p1, Matrix2 p2) {return (p1.X!=p2.X || p1.Y!=p2.Y);}
		#endregion

		#region Methods
		public override bool Equals(object obj)
		{
			return obj != null && (Matrix2)obj == this;
		}

		public override string ToString()
		{
			return string.Format("{0} : {1} : {2} : {3}", X, Y);
		}

		public override int GetHashCode()
		{
			return base.GetHashCode();
		}

		public static Matrix2 Identity()
		{
			return new Matrix2(new Vector2(1,0), new Vector2(0,1));
		}

		public Matrix2 Transpose()
		{
			return new Matrix2(new Vector2(X.X, Y.X), new Vector2(X.Y, Y.Y));
		}

		public Matrix2 Abs()
		{
			return new Matrix2(X.Abs(), Y.Abs());
		}

		public Matrix2 Multiply(Matrix2 matrix)
		{
			return new Matrix2(
				new Vector2((matrix.X.X*X.X) + (matrix.X.Y*Y.X), (matrix.X.X*X.Y) + (matrix.X.Y*Y.Y)),
				new Vector2((matrix.Y.X*X.X) + (matrix.Y.Y*Y.X), (matrix.Y.X*X.Y) + (matrix.Y.Y*Y.Y)));
		}

		public Matrix2 Normalize()
		{
			return new Matrix2(X.Normalize(), Y.Normalize());
		}

		public Matrix2 NormalizeSafe()
		{
			return new Matrix2(X.NormalizeSafe(), Y.NormalizeSafe());
		}

		public Matrix2 Rotate(Num radians)
		{
			Num tCos = (Num)MathS.Cos(radians), tSin = (Num)MathS.Sin(radians);
			return new Matrix2(
				new Vector2((X.X*tCos) - (X.Y*tSin), (X.X*tSin) + (X.Y*tCos)),
				new Vector2((Y.X*tCos) - (Y.Y*tSin), (Y.X*tSin) + (Y.Y*tCos)));
		}
		#endregion
	}
}